<template>
  <iframe
    ref="frameRef"
    sandbox="allow-same-origin allow-scripts"
    referrerpolicy="no-referrer"
    style="width:100%;border:0;display:block;background:transparent;"
    :style="{ height: boxH + 'px' }"
  ></iframe>
</template>

<script setup lang="ts">
import { ref, watch, onMounted, nextTick } from 'vue'
import DOMPurify from 'dompurify'

const props = withDefaults(defineProps<{
  html: string
  baseUrl?: string
  minHeight?: string
  maxHeight?: string
  /** 只按 A4 的“上半部分”来计算缩放，数值越小，显示越大（0~1） */
  cropTopRatio?: number
}>(), {
  baseUrl: '',
  minHeight: '240px',
  maxHeight: '300px',         // 给点更大高度，观感更好
  cropTopRatio: 0.45,         // 只看上面 45% 决定缩放，底部被裁掉（更易读）
})

const frameRef = ref<HTMLIFrameElement>()
const boxH = ref(260)

// A4（≈96dpi）
const A4W = 794, A4H = 1123
const FIT_RATIO = 0.94         // 留 6% 安全边距

function extractBody(raw: string): string {
  if (!raw) return ''
  const m = raw.match(/<body[^>]*>([\s\S]*?)<\/body>/i)
  return m ? m[1] : raw
}

function sanitizeStatic(raw: string): string {
  const src = extractBody(raw)
  return DOMPurify.sanitize(src, {
    USE_PROFILES: { html: true },
    ADD_TAGS: ['style'],
    ADD_ATTR: [
      'style','class','id','align','valign','width','height',
      'colspan','rowspan','cellpadding','cellspacing','border'
    ],
    FORBID_TAGS: ['script'],
    FORBID_ATTR: [/^on/i],
    ALLOWED_URI_REGEXP: /^(https?|data):/i,
    KEEP_CONTENT: true,
  })
}

const buildSrcdoc = (safeHtml: string, scale: number) => {
  const baseTag = props.baseUrl ? `<base href="${props.baseUrl}">` : ''
  const css = `
    :root { color-scheme: light; }
    html,body { margin:0; padding:0; background:transparent; overflow:hidden; }
    @supports (display:block) { }
  `
  const style2 = `

    /* 顶部水平居中 + 等比缩放；超出底部自动裁掉 */
    .paper {
      position:absolute; top:0; left:50%;
      width:${A4W}px; height:${A4H}px;
      background:#fff; border:1px solid #e5e7eb; border-radius:6px;
      transform:translateX(-50%) scale(${scale});
      transform-origin: top center; box-shadow:none; overflow:hidden;
    }
    .preview-root {
      box-sizing:border-box; width:${A4W}px; height:${A4H}px;
      padding:28px 32px;
      font-family: system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,
                   "Noto Sans","PingFang SC","Hiragino Sans GB","Microsoft YaHei",SimSun,serif;
      font-size:14px; line-height:1.65; color:#222;
      -webkit-font-smoothing:antialiased; -moz-osx-font-smoothing:grayscale;
    }
    .preview-root * { box-sizing:border-box; max-width:100%; }
    .preview-root h1{font-size:24px;margin:12px 0;}
    .preview-root h2{font-size:20px;margin:10px 0;}
    .preview-root h3{font-size:18px;margin:8px 0;}
    .preview-root p {margin:6px 0;}
    .preview-root table{border-collapse:collapse;width:100%;}
    .preview-root img,svg,video,canvas,audio{display:block;}
    .preview-root > * { margin-left:0.01px; margin-right:0.01px; }
  `
  return `<!doctype html>
<html>
<head>
<meta charset="utf-8">
${baseTag}
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src data: https: http:; style-src 'unsafe-inline' https: http:; font-src data: https: http:; script-src 'none'; connect-src 'none';">
<style>${css}</style>
<style>${style2}</style>
</head>
<body>
  <div class="stage">
    <div class="paper">
      <div class="preview-root">${safeHtml}</div>
    </div>
  </div>
</body>
</html>`
}

const render = async () => {
  await nextTick()
  const iframe = frameRef.value
  if (!iframe) return
  const box = iframe.parentElement
  if (!box) return

  const minH = toPx(props.minHeight), maxH = toPx(props.maxHeight)
  const targetH = clamp(box.clientHeight || maxH, minH, maxH)
  boxH.value = targetH

  const innerW = (box.clientWidth || 320) - 2
  const innerH = targetH - 2

  // 关键：只按“上部分高度”计算缩放，能放大显示顶部内容
  const effectiveAH = A4H * clamp(props.cropTopRatio ?? 1, 0.2, 1)  // 最少取 20% 防止过大
  const scale = Math.max(0.1, Math.min(innerW / A4W, innerH / effectiveAH)) * FIT_RATIO

  const safe = sanitizeStatic(props.html || '')
  const doc = iframe.contentDocument || iframe.contentWindow?.document
  if (!doc) return
  doc.open()
  doc.write(buildSrcdoc(safe, scale))
  doc.close()
}

onMounted(() => {
  render()
  const box = frameRef.value?.parentElement
  if (box && 'ResizeObserver' in window) {
    const ro = new ResizeObserver(() => render())
    ro.observe(box)
  }
})
watch(() => props.html, () => render())

function toPx(v?: string) {
  if (!v) return 0
  if (v.endsWith('vh')) return Math.round(window.innerHeight * (parseFloat(v)/100))
  if (v.endsWith('px')) return parseInt(v)
  const n = parseInt(v); return isNaN(n) ? 0 : n
}
const clamp = (v:number, a:number, b:number) => Math.max(a, Math.min(v, b))
</script>
